﻿#pragma warning disable 1591
namespace SessionHunter.Business.SessionReading
{

    // UNDONE: Commenting this out so we can use the Client profile
    //using System.Web.SessionState;
    // UNDONE: Commenting this out so we can use the Client profile
    // using System.Web;
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Collections.Specialized;
    using System.Diagnostics;
    using System.Diagnostics.Contracts;
    using System.Globalization;
    using System.IO;
    using System.Reflection;
    using System.Runtime.Serialization;
    using System.Runtime.Serialization.Formatters.Binary;
    // UNDONE: Commenting this out so we can use the Client profile
    //using System.Web.Util;
    using System.Security.Permissions;
    using SessionHunter.Model;

    /// <summary>
    /// A collection of objects stored in session state
    /// </summary>
    /// <remarks>
    /// This is the insanest thing I've ever done.  It is the reverse engineered source of the SessionStateItemCollection so that I can get access to the raw data
    /// that comes out when the data is deserialized.  I use this to get the size of the object as it is pulled out of the DB instead of trying to determine the size
    /// after the fact.  I would much rather write my own class, but it is pretty tricky internal knowledge stuff so this was a shortcut.
    /// </remarks>
    // UNDONE: Commenting this out so we can use the Client profile
    //[AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2229:ImplementSerializationConstructors"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2240:ImplementISerializableCorrectly"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1001:TypesThatOwnDisposableFieldsShouldBeDisposable"), Serializable]
    public sealed class SessionStateItemCollection_Custom : NameObjectCollectionBase, ISessionStateItemCollection
    {
        private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        #region New Code
        /// <summary>
        /// How many session items get processed before the next event is raised.
        /// </summary>
        public const int SessionItemProcessedCounter = 10;
        #endregion

        #region External functions
        [ContractOption("contract", "inheritance", false)]
        [ContractOption("runtime", "checking", false)]
        internal static object ReadValueFromStream(BinaryReader reader)
        {
            object obj = null;
            switch (reader.ReadByte() - 1)
            {
                case 0:
                    {
                        obj = reader.ReadString();
                        goto IL_22D;
                    }
                case 1:
                    {
                        obj = (int)reader.ReadInt32();
                        goto IL_22D;
                    }
                case 2:
                    {
                        obj = reader.ReadBoolean();
                        goto IL_22D;
                    }
                case 3:
                    {
                        obj = new DateTime(reader.ReadInt64());
                        goto IL_22D;
                    }
                case 4:
                    {
                        //UNDONE: Re-wrote this code so it compiles
                        /*
                        int[] array = new int[4];
                        int i = 0;
                        while (i < 4)
                        {
                            array[i] = reader.ReadInt32();
                            i++;
                            continue;
                        Block_3:
                            obj = new decimal(array);
                            goto IL_22D;
                        }
                        goto Block_3;
                         */
                        int[] array = new int[4];
                        int i = 0;
                        while (i < 4)
                        {
                            array[i] = reader.ReadInt32();
                            i++;
                        }
                        obj = new decimal(array);
                        goto IL_22D;
                    }
                case 5:
                    {
                        obj = (byte)reader.ReadByte();
                        goto IL_22D;
                    }
                case 6:
                    {
                        obj = (char)reader.ReadChar();
                        goto IL_22D;
                    }
                case 7:
                    {
                        obj = reader.ReadSingle();
                        goto IL_22D;
                    }
                case 8:
                    {
                        obj = reader.ReadDouble();
                        goto IL_22D;
                    }
                case 9:
                    {
                        obj = (sbyte)reader.ReadSByte();
                        goto IL_22D;
                    }
                case 10:
                    {
                        obj = (short)reader.ReadInt16();
                        goto IL_22D;
                    }
                case 11:
                    {
                        obj = (long)reader.ReadInt64();
                        goto IL_22D;
                    }
                case 12:
                    {
                        obj = (ushort)reader.ReadUInt16();
                        goto IL_22D;
                    }
                case 13:
                    {
                        obj = (uint)reader.ReadUInt32();
                        goto IL_22D;
                    }
                case 14:
                    {
                        obj = (ulong)reader.ReadUInt64();
                        goto IL_22D;
                    }
                case 15:
                    {
                        obj = new TimeSpan(reader.ReadInt64());
                        goto IL_22D;
                    }
                case 16:
                    {
                        obj = new Guid(reader.ReadBytes(16));
                        goto IL_22D;
                    }
                case 17:
                    {
                        obj = ((IntPtr.Size != 4) ? new IntPtr(reader.ReadInt64()) : new IntPtr(reader.ReadInt32()));
                        goto IL_22D;
                    }
                case 18:
                    {
                        obj = ((UIntPtr.Size != 4) ? new UIntPtr(reader.ReadUInt64()) : new UIntPtr(reader.ReadUInt32()));
                        goto IL_22D;
                    }
                case 19:
                    {
                        // Original
                        // obj = new BinaryFormatter().Deserialize(reader.BaseStream);
                        #region New Code
                        var deserializer = new DynamicDeserializer();
                        try
                        {
                            // New
                            BinaryFormatter formatter = new BinaryFormatter();
                            formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
                            formatter.Binder = deserializer;
                            // This will raise a SerializationException if it can't find the assembly
                            obj = formatter.Deserialize(reader.BaseStream);
                        }
                        catch (TargetInvocationException tie)
                        {
                            obj = new TargetInvocationWrapper(deserializer.SessionTypeName);
                            logger.Warn("Catching and handling a TargetInvocationException during deserialization of type [" + deserializer.SessionTypeName + "]");
                            logger.Debug("Ignored TargetInvocationException was " + tie);
                        }
                        #endregion
                        goto IL_22D;
                    }
                case 20:
                    {
                        obj = null;
                        goto IL_22D;
                    }
            }
        IL_22D:
            return obj;
        }

        private static StringComparer s_caseInsensitiveInvariantKeyComparer;

        internal static StringComparer CaseInsensitiveInvariantKeyComparer
        {
            get
            {
                if (s_caseInsensitiveInvariantKeyComparer == null)
                {
                    s_caseInsensitiveInvariantKeyComparer = StringComparer.Create(CultureInfo.InvariantCulture, true);
                }
                return s_caseInsensitiveInvariantKeyComparer;
            }
        }
        #endregion

        class KeyedCollection : NameObjectCollectionBase
        {

            internal KeyedCollection(int count)
                : base(count, CaseInsensitiveInvariantKeyComparer)
            {
            }

            internal Object this[String name]
            {
                get
                {
                    return BaseGet(name);
                }

                set
                {
                    Object oldValue = BaseGet(name);
                    if (oldValue == null && value == null)
                        return;

                    BaseSet(name, value);
                }
            }

            internal Object this[int index]
            {
                get
                {
                    return BaseGet(index);
                }

                set
                {
                    Object oldValue = BaseGet(index);

                    // We don't expect null value
                    Debug.Assert(value != null);

                    BaseSet(index, value);
                }
            }

            internal void Remove(String name)
            {
                BaseRemove(name);
            }

            internal void RemoveAt(int index)
            {
                BaseRemoveAt(index);
            }

            internal void Clear()
            {
                BaseClear();
            }

            internal string GetKey(int index)
            {
                return BaseGetKey(index);
            }

            internal bool ContainsKey(string name)
            {
                // Please note that we don't expect null value to be inserted.
                return (BaseGet(name) != null);
            }
        }

        static Hashtable s_immutableTypes;
        const int NO_NULL_KEY = -1;
        const int SIZE_OF_INT32 = 4;
        bool _dirty;
        KeyedCollection _serializedItems;
        Stream _stream;
        int _iLastOffset;
        object _serializedItemsLock = new object();

        public SessionStateItemCollection_Custom()
            : base(CaseInsensitiveInvariantKeyComparer)
        {
        }

        static SessionStateItemCollection_Custom()
        {
            Type t;
            s_immutableTypes = new Hashtable(19);

            t = typeof(String);
            s_immutableTypes.Add(t, t);
            t = typeof(Int32);
            s_immutableTypes.Add(t, t);
            t = typeof(Boolean);
            s_immutableTypes.Add(t, t);
            t = typeof(DateTime);
            s_immutableTypes.Add(t, t);
            t = typeof(Decimal);
            s_immutableTypes.Add(t, t);
            t = typeof(Byte);
            s_immutableTypes.Add(t, t);
            t = typeof(Char);
            s_immutableTypes.Add(t, t);
            t = typeof(Single);
            s_immutableTypes.Add(t, t);
            t = typeof(Double);
            s_immutableTypes.Add(t, t);
            t = typeof(SByte);
            s_immutableTypes.Add(t, t);
            t = typeof(Int16);
            s_immutableTypes.Add(t, t);
            t = typeof(Int64);
            s_immutableTypes.Add(t, t);
            t = typeof(UInt16);
            s_immutableTypes.Add(t, t);
            t = typeof(UInt32);
            s_immutableTypes.Add(t, t);
            t = typeof(UInt64);
            s_immutableTypes.Add(t, t);
            t = typeof(TimeSpan);
            s_immutableTypes.Add(t, t);
            t = typeof(Guid);
            s_immutableTypes.Add(t, t);
            t = typeof(IntPtr);
            s_immutableTypes.Add(t, t);
            t = typeof(UIntPtr);
            s_immutableTypes.Add(t, t);
        }

        static internal bool IsImmutable(Object o)
        {
            return s_immutableTypes[o.GetType()] != null;
        }

        [ContractRuntimeIgnored]
        internal void DeserializeAllItems()
        {
            if (_serializedItems == null)
            {
                return;
            }

            lock (_serializedItemsLock)
            {
                for (int i = 0; i < _serializedItems.Count; i++)
                {
                    DeserializeItem(_serializedItems.GetKey(i), false);
                }
            }
        }

        [ContractOption("contract", "inheritance", false)]
        [ContractOption("runtime", "checking", false)]
        void DeserializeItem(int index)
        {
            // No-op if SessionStateItemCollection_Custom is not deserialized from a persistent storage.
            if (_serializedItems == null)
            {
                return;
            }

#if DBG 
            // The keys in _serializedItems should match the beginning part of
            // the list in NameObjectCollectionBase 
            for (int i=0; i < _serializedItems.Count; i++) { 
                Debug.Assert(_serializedItems.GetKey(i) == BaseGetKey(i));
            } 
#endif

            lock (_serializedItemsLock)
            {
                // No-op if the item isn't serialized. 
                if (index >= _serializedItems.Count)
                {
                    return;
                }

                DeserializeItem(_serializedItems.GetKey(index), false);
            }
        }

        // UNDONE: Commenting this out so we can use the Client profile
        //[AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)]
        private object ReadValueFromStreamWithAssert()
        {
            return ReadValueFromStream(new BinaryReader(_stream));
        }

        [ContractOption("contract", "inheritance", false)]
        [ContractOption("runtime", "checking", false)]
        void DeserializeItem(String name, bool check)
        {
            int offset;
            object val;

            lock (_serializedItemsLock)
            {
                if (check)
                {
                    // No-op if SessionStateItemCollection_Custom is not deserialized from a persistent storage, 
                    if (_serializedItems == null)
                    {
                        return;
                    }

                    // User is asking for an item we don't have.
                    if (!_serializedItems.ContainsKey(name))
                    {
                        return;
                    }
                }

                Debug.Assert(_serializedItems != null);
                Debug.Assert(_stream != null);

                offset = (int)_serializedItems[name];
                if (offset < 0)
                {
                    // It has been deserialized already.
                    return;
                }

                // Position the stream to the place where the item is stored. 
                _stream.Seek(offset, SeekOrigin.Begin);

                // Set the value
                // UNDONE: Could not find reference
                // Debug.Trace("SessionStateItemCollection_Custom", "Deserialized an item: keyname=" + name);

                // VSWhidbey 427316: Sandbox Serialization in non full trust cases 
                // UNDONE: This probably does not apply to us and can't find reference
                /*
                if (HttpRuntime.NamedPermissionSet != null && HttpRuntime.ProcessRequestInApplicationTrust)
                {
                    HttpRuntime.NamedPermissionSet.PermitOnly();
                }*/

                // This deserialization work used to be done in AcquireRequestState event when
                // there is no user code on the stack.
                // In whidbey we added this on-demand deserialization for performance reason.  However, 
                // in medium and low trust cases the page doesn't have permission to do it.
                // So we have to assert the permission. 
                // (See VSWhidbey 275003) 
                val = ReadValueFromStreamWithAssert();

                BaseSet(name, val);

                // At the end, mark the item as deserialized by making it -ve.
                // If the offset is zero, we will use -ve Int32.MaxValue to mark it 
                // as deserialized.
                if (offset == 0)
                {
                    _serializedItems[name] = Int32.MaxValue * -1;
                }
                else if (offset > 0)
                {
                    _serializedItems[name] = offset * -1;
                }
                else
                {
                    Debug.Fail("offset should never be zero within locked deserializeItem!");
                }
            }

        }

        void MarkItemDeserialized(String name)
        {
            // No-op if SessionStateItemCollection_Custom is not deserialized from a persistent storage,
            if (_serializedItems == null)
            {
                return;
            }

            lock (_serializedItemsLock)
            {
                // If the serialized collection contains this key,
                // set it as deserialized by making it -ve. 
                if (_serializedItems.ContainsKey(name))
                {
                    int offset = (int)_serializedItems[name];

                    // Mark the item as deserialized by making it -ve. 
                    // If the offset is zero, we will use -ve Int32.MaxValue to mark it
                    // as deserialized. 

                    if (offset == 0)
                    {
                        _serializedItems[name] = Int32.MaxValue * -1;
                    }
                    else if (offset > 0)
                    {
                        _serializedItems[name] = offset * -1;
                    }
                }
            }
        }

        void MarkItemDeserialized(int index)
        {
            // No-op if SessionStateItemCollection_Custom is not deserialized from a persistent storage,
            if (_serializedItems == null)
            {
                return;
            }

#if DBG 
            // The keys in _serializedItems should match the beginning part of 
            // the list in NameObjectCollectionBase
            for (int i=0; i < _serializedItems.Count; i++) { 
                Debug.Assert(_serializedItems.GetKey(i) == BaseGetKey(i));
            }
#endif

            // No-op if the item isn't serialized.
            if (index >= _serializedItems.Count)
            {
                return;
            }

            lock (_serializedItemsLock)
            {
                int offset = (int)_serializedItems[index];

                // Mark the item as deserialized by making it -ve. 
                // If the offset is zero, we will use -ve Int32.MaxValue to mark it
                // as deserialized. 

                if (offset == 0)
                {
                    _serializedItems[index] = Int32.MaxValue * -1;
                }
                else if (offset > 0)
                {
                    _serializedItems[index] = offset * -1;
                }
            }
        }

        public bool Dirty
        {
            get { return _dirty; }
            set { _dirty = value; }
        }

        public Object this[String name]
        {
            get
            {
                DeserializeItem(name, true);

                Object obj = BaseGet(name);
                if (obj != null)
                {
                    if (!IsImmutable(obj))
                    {
                        // If the item is immutable (e.g. an array), then the caller has the ability to change
                        // its content without calling our setter.  So we have to mark the collection 
                        // as dirty.
                        // UNDONE: Could not find reference
                        // Debug.Trace("SessionStateItemCollection_Custom", "Setting _dirty to true in get");
                        _dirty = true;
                    }
                }

                return obj;
            }

            set
            {
                MarkItemDeserialized(name);
                // UNDONE: Could not find reference
                // Debug.Trace("SessionStateItemCollection_Custom", "Setting _dirty to true in set");
                BaseSet(name, value);
                _dirty = true;
            }
        }

        [ContractOption("contract", "inheritance", false)]
        [ContractOption("runtime", "checking", false)]
        public Object this[int index]
        {
            get
            {
                DeserializeItem(index);

                Object obj = BaseGet(index);
                if (obj != null)
                {
                    if (!IsImmutable(obj))
                    {
                        // UNDONE: Could not find reference
                        // Debug.Trace("SessionStateItemCollection_Custom", "Setting _dirty to true in get");
                        _dirty = true;
                    }
                }

                return obj;
            }

            set
            {
                MarkItemDeserialized(index);
                // UNDONE: Could not find reference
                // Debug.Trace("SessionStateItemCollection_Custom", "Setting _dirty to true in set");
                BaseSet(index, value);
                _dirty = true;
            }
        }


        [ContractOption("contract", "inheritance", false)]
        [ContractOption("runtime", "checking", false)]
        public void Remove(String name)
        {
            lock (_serializedItemsLock)
            {
                if (_serializedItems != null)
                {
                    _serializedItems.Remove(name);
                }

                BaseRemove(name);
                _dirty = true;
            }
        }

        [ContractOption("contract", "inheritance", false)]
        [ContractOption("runtime", "checking", false)]
        public void RemoveAt(int index)
        {
            lock (_serializedItemsLock)
            {
                if (_serializedItems != null && index < _serializedItems.Count)
                {
                    _serializedItems.RemoveAt(index);
                }

                BaseRemoveAt(index);
                _dirty = true;
            }
        }

        [ContractOption("contract", "inheritance", false)]
        [ContractOption("runtime", "checking", false)]
        public void Clear()
        {
            lock (_serializedItemsLock)
            {
                if (_serializedItems != null)
                {
                    _serializedItems.Clear();
                }
                BaseClear();
                _dirty = true;
            }
        }

        public override IEnumerator GetEnumerator()
        {
            // Have to deserialize all items; otherwise the enumerator won't
            // work because we'll keep on changing the collection during 
            // individual item deserialization
            DeserializeAllItems();

            return base.GetEnumerator();
        }

        public override NameObjectCollectionBase.KeysCollection Keys
        {
            get
            {
                // Unfortunately, we have to deserialize all items first, because 
                // Keys.GetEnumerator might be called and we have the same problem
                // as in GetEnumerator() above.
                DeserializeAllItems();

                return base.Keys;
            }
        }

        // UNDONE: Don't need this because all we care about is getting the data out
        /*
        [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.SerializationFormatter)]
        private void WriteValueToStreamWithAssert(object value, BinaryWriter writer)
        {
            WriteValueToStream(value, writer);
        }

        public void Serialize(BinaryWriter writer)
        {
            int count;
            int i;
            long iOffsetStart;
            long iValueStart;
            string key;
            object value;
            long curPos;
            byte[] buffer = null;
            Stream baseStream = writer.BaseStream;

            // VSWhidbey 427316: Sandbox Serialization in non full trust cases
            if (HttpRuntime.NamedPermissionSet != null && HttpRuntime.ProcessRequestInApplicationTrust)
            {
                HttpRuntime.NamedPermissionSet.PermitOnly();
            }

            lock (_serializedItemsLock)
            {
                count = Count;
                writer.Write(count);

                if (count > 0)
                {
                    if (BaseGet(null) != null)
                    {
                        // We have a value with a null key.  Find its index.
                        for (i = 0; i < count; i++)
                        {
                            key = BaseGetKey(i);
                            if (key == null)
                            {
                                writer.Write(i);
                                break;
                            }
                        }

                        Debug.Assert(i != count);
                    }
                    else
                    {
                        writer.Write(NO_NULL_KEY);
                    }

                    // Write out all the keys. 
                    for (i = 0; i < count; i++)
                    {
                        key = BaseGetKey(i);
                        if (key != null)
                        {
                            writer.Write(key);
                        }
                    }

                    // Next, allocate space to store the offset: 
                    // - We won't store the offset of first item because it's always zero. 
                    // - The offset of an item is counted from the beginning of serialized values
                    // - But we will store the offset of the first byte off the last item because 
                    //   we need that to calculate the size of the last item.
                    iOffsetStart = baseStream.Position;
                    baseStream.Seek(SIZE_OF_INT32 * count, SeekOrigin.Current);

                    iValueStart = baseStream.Position;

                    for (i = 0; i < count; i++)
                    {
                        // See if that item has not be deserialized yet.
                        if (_serializedItems != null &&
                            i < _serializedItems.Count &&
                            (int)_serializedItems[i] >= 0)
                        {

                            int dataLength;
                            int offset1, offset2;

                            Debug.Assert(_stream != null);

                            // The item is read as serialized data from a store, and it's still 
                            // serialized, meaning no one has referenced it.  Just copy
                            // the bytes over.

                            // The length of the item is the difference between its offset 
                            // and next item offset, which are stored at position i and i+1 respectively,
                            // except for the last items. 
                            offset1 = (int)_serializedItems[i];

                            if (i == _serializedItems.Count - 1)
                            {
                                offset2 = _iLastOffset;
                            }
                            else
                            {
                                offset2 = Math.Abs((int)_serializedItems[i + 1]);
                            }

                            dataLength = offset2 - offset1;

                            // Move the stream to the serialized data and copy it over to writer 
                            _stream.Seek(offset1, SeekOrigin.Begin);

                            if (buffer == null || buffer.Length < dataLength)
                            {
                                buffer = new Byte[dataLength];
                            }

#if DBG 
                            int read =
#endif
                            _stream.Read(buffer, 0, dataLength);
#if DBG
                            Debug.Assert(read == dataLength);
#endif

                            baseStream.Write(buffer, 0, dataLength);
                        }
                        else
                        {
                            value = BaseGet(i);
                            WriteValueToStreamWithAssert(value, writer);
                        }

                        curPos = baseStream.Position;

                        // Write the offset 
                        baseStream.Seek(i * SIZE_OF_INT32 + iOffsetStart, SeekOrigin.Begin);
                        writer.Write((int)(curPos - iValueStart));

                        // Move back to current position
                        baseStream.Seek(curPos, SeekOrigin.Begin);

                        Debug.Trace("SessionStateItemCollection_Custom",
                            "Serialize: curPost=" + curPos + ", offset= " + (int)(curPos - iValueStart));
                    }
                }
#if DBG
                writer.Write((byte)0xff); 
#endif
            }
        }
         */

        /// <summary>
        /// Deserializes to session using the internal knowledge and populates a list of session items we can use.
        /// The advantages of this method are:
        /// -I can get the size on the fly
        /// -If some objects cannot be resolved due to bad references, I can still find the other objects
        /// </summary>
        /// <param name="reader">The reader that points to the session DB.</param>
        /// <param name="sessionsProcessed">Called whenver sessions are processed.</param>
        /// <param name="sessionItems">A list of session items, which may be 0 in size.</param>
        /// <returns>
        /// True if there were no errors
        /// </returns>
        public static bool DeserializeToSessionItem(BinaryReader reader, Action<int> sessionsProcessed, out List<SessionItem> sessionItems)
        {
            Contract.Requires(reader != null);
            Contract.Ensures(Contract.ValueAtReturn(out sessionItems) != null, "Cannot return a null value from DeserializeToSessionItem");

            // Call the reverse engineered method
            var d = Deserialize(reader, sessionsProcessed);
            var count = d.Count;
            // Create a list based on the number of items we are putting in
            sessionItems = new List<SessionItem>(count);
            var retVal = true;

            // Iterate through the session objects, getting the key and size
            for (int i = 0; i < count; i++)
            {
                var sessionItem = new SessionItem();

                // We have to call BaseGetKey, because if you call the Key, it will deserialize the data
                sessionItem.Key = d.BaseGetKey(i);
                // Calculate the size by comparing the offsets into the data
                if (i < count - 1)
                {
                    // All items except the last one, compare two offsets
                    sessionItem.Size = (int)d._serializedItems[i + 1] - (int)d._serializedItems[i] - 1;
                }
                else
                {
                    // Last item, use the last offset value
                    sessionItem.Size = (int)d._iLastOffset - (int)d._serializedItems[i] - 1;
                }

                sessionItems.Add(sessionItem);
            }

            var sessionCounter = 0;
            // We have to get the data in a seperate sweep, because the call do d[] deserializes the data, which changes the values in _serializedItems
            foreach (var item in sessionItems)
            {
                // Raise a message every SessionItemProcessedCounter
                if (sessionCounter > 0 && sessionCounter % SessionItemProcessedCounter == 0)
                {
                    if (sessionsProcessed != null)
                    {
                        sessionsProcessed(sessionCounter);
                    }
                }

                try
                {
                    item.Item = d[item.Key];
                    if (item.Item is TargetInvocationWrapper)
                    {
                        item.ShortTypeName = (item.Item as TargetInvocationWrapper).VariableType;
                    }
                    else
                    {
                        item.ShortTypeName = item.Item.GetType().Name;
                    }
                }
                catch (SerializationException sx)
                {
                    logger.LogException(NLog.LogLevel.Warn, "Error trying to deserialize item", sx);
                    item.ShortTypeName = Resources.Strings.ReferenceError_ItemMessage;
                    item.ErrorMessage = sx.Message;
                    retVal = false;
                }

                sessionCounter++;
            }

            return retVal;
        }

        /// <summary>
        /// Deserializes the session data.  It is essentially unchanged in terms of algorithm from the original,
        /// except for some logging and exception changes
        /// </summary>
        /// <param name="reader">The reader with the session data</param>
        /// <param name="sessionsProcessed">Called whenver sessions are processed.</param>
        /// <returns>
        /// A streamed list of session objects
        /// </returns>
        public static SessionStateItemCollection_Custom Deserialize(BinaryReader reader, Action<int> sessionsProcessed)
        {
            SessionStateItemCollection_Custom d = new SessionStateItemCollection_Custom();
            int count;
            int nullKey;
            String key;
            int i;
            byte[] buffer;

            count = reader.ReadInt32();

            if (count > 0)
            {
                nullKey = reader.ReadInt32();

                d._serializedItems = new KeyedCollection(count);

                // First, deserialize all the keys
                for (i = 0; i < count; i++)
                {
                    if (i == nullKey)
                    {
                        key = null;
                    }
                    else
                    {
                        key = reader.ReadString();
                    }

                    // Need to set them with null value first, so that
                    // the order of them items is correct.
                    d.BaseSet(key, null);
                }

                // Next, deserialize all the offsets 
                for (i = 0; i < count; i++)
                {
                    if (i == 0)
                    {
                        d._serializedItems[d.BaseGetKey(i)] = 0;
                    }
                    else
                    {
                        d._serializedItems[d.BaseGetKey(i)] = reader.ReadInt32();
                    }
                }

                //
                d._iLastOffset = reader.ReadInt32();

                // UNDONE: Could not find reference
                //Debug.Trace("SessionStateItemCollection_Custom", "Deserialize: _iLastOffset= " + d._iLastOffset);
                logger.Debug("SessionStateItemCollection_Custom", "Deserialize: _iLastOffset= " + d._iLastOffset);

                // _iLastOffset is the first byte past the last item, which equals
                // the total length of all serialized data 
                buffer = new byte[d._iLastOffset];
                int bytesRead = reader.BaseStream.Read(buffer, 0, d._iLastOffset);
                if (bytesRead != d._iLastOffset)
                {
                    // UNDONE: Could not find reference to string
                    // throw new HttpException(SR.GetString(SR.Invalid_session_state));
                    throw new ApplicationException("Invalid_session_state");
                }
                d._stream = new MemoryStream(buffer);
            }

#if DBG 
            Debug.Assert(reader.ReadByte() == 0xff); 
#endif

            d._dirty = false;

            return d;
        }
    }

}
#pragma warning restore 1591
